Design First
The Design-First approach (also known as top-down) means you define your API contract first—typically using OpenAPI/Swagger, RAML, or API Blueprint—before writing any backend code. This contract serves as a single source of truth for developers, frontend/backend teams, QA, and clients.
Pros of Design-First Approach
- Clear contract: Ensures all teams agree on how the API behaves before code is written.
- Documentation first: You get clean API docs from day one.
- Stub generation: Can generate boilerplate server code & client SDKs automatically.
- Validation: Helps validate requests/responses against a defined schema.
Tools You Can Use
- OpenAPI/Swagger: Specification language for REST APIs.
- Swagger Editor: Online tool to design API visually and export YAML/JSON.
- swagger-codegen or openapi-generator: Generate Node.js server stubs.
- express-openapi-validator: Middleware to enforce schema validation.
Example of Design-First Approach
Create openapi.yaml file
openapi: 3.0.0
info:
title: Design-First API Example
version: 1.0.0
paths:
/users:
get:
summary: Get all users
responses:
"200":
description: A list of users
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/User"
post:
summary: Create a new user
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UserInput"
responses:
"201":
description: User created
content:
application/json:
schema:
$ref: "#/components/schemas/User"
components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: string
UserInput:
type: object
properties:
name:
type: string
required:
- name
index.js
const express = require("express");
const YAML = require("yamljs");
const { OpenApiValidator } = require("express-openapi-validator");
const apiSpec = YAML.load("./openapi.yaml");
const app = express();
app.use(express.json());
// Serve the OpenAPI spec
app.use("/spec", (req, res) => res.json(apiSpec));
// Init OpenAPI Validator
new OpenApiValidator({
apiSpec: "./openapi.yaml",
validateRequests: true, // auto validate request bodies
validateResponses: true, // auto validate responses
})
.install(app)
.then(() => {
// Routes go after validator is set up
const userRoutes = require("./routes/users");
app.use("/users", userRoutes);
app.use((err, req, res, next) => {
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
app.listen(3000, () => {
console.log("API running on http://localhost:3000");
console.log("Docs available at Swagger Editor or Redoc");
});
});
routes/users.js
const express = require("express");
const router = express.Router();
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];
router.get("/", (req, res) => {
res.status(200).json(users);
});
router.post("/", (req, res) => {
const { name } = req.body;
const newUser = { id: users.length + 1, name };
users.push(newUser);
res.status(201).json(newUser);
});
module.exports = router;
Differences from Code-First
| Feature | Design-First | Code-First |
|---|---|---|
| Start Point | API Contract (YAML/JSON) | Application Code (routes) |
| Tooling | Swagger Editor, OpenAPI Generator | Swagger JSDoc, Comment Parsers |
| Contract Authority | Spec is the source of truth | Code is the source of truth |
| Ideal for | Team collaboration, SDK generation, QA | Quick prototyping, smaller teams |